<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>列表排序算法可视化工具（修正版）</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', sans-serif;
            margin: 20px;
            line-height: 1.6;
            background-color: #f9f9f9;
        }
        .container {
            max-width: 1000px;
            margin: 0 auto;
            padding: 25px;
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 0 15px rgba(0,0,0,0.1);
        }
        h1, h2, h3 {
            color: #2c3e50;
            margin-top: 0;
        }
        h1 {
            color: #3498db;
            border-bottom: 2px solid #3498db;
            padding-bottom: 10px;
        }
        .list-container {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            padding: 20px;
            background-color: #f8f9fa;
            border-radius: 8px;
            margin-bottom: 25px;
            min-height: 70px;
            align-items: center;
        }
        .item {
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-weight: bold;
            position: relative;
            transition: all 0.3s ease;
            font-size: 16px;
        }
        .original .item {
            background-color: #e3f2fd;
            border-color: #64b5f6;
            color: #1976d2;
        }
        .subset .item {
            background-color: #fff9c4;
            border-color: #ffd54f;
            color: #ff8f00;
        }
        .result .item {
            background-color: #c8e6c9;
            border-color: #81c784;
            color: #2e7d32;
        }
        .pointer {
            position: absolute;
            top: -28px;
            color: #e53935;
            font-size: 14px;
            font-weight: bold;
            white-space: nowrap;
        }
        .controls {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
            margin: 25px 0;
            justify-content: center;
        }
        button {
            padding: 10px 18px;
            background-color: #4285f4;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 15px;
            transition: all 0.3s;
            min-width: 100px;
        }
        button:hover {
            background-color: #3367d6;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        button:disabled {
            background-color: #bdc3c7;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        .explanation {
            padding: 20px;
            background-color: #f8f9fa;
            border-radius: 8px;
            margin-top: 25px;
            border-left: 5px solid #4285f4;
        }
        .highlight {
            background-color: #ffab91 !important;
            transform: scale(1.15);
            box-shadow: 0 0 12px rgba(255, 152, 0, 0.6);
            z-index: 10;
        }
        .added {
            animation: fadeIn 0.6s ease-out;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-15px) scale(0.9); }
            to { opacity: 1; transform: translateY(0) scale(1); }
        }
        .config-panel {
            background-color: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 25px;
            border: 1px solid #e0e0e0;
        }
        .input-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
            color: #34495e;
        }
        input {
            padding: 10px;
            width: 100%;
            box-sizing: border-box;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-size: 15px;
            transition: border 0.3s;
        }
        input:focus {
            border-color: #4285f4;
            outline: none;
        }
        .speed-control {
            display: flex;
            align-items: center;
            gap: 15px;
            margin-top: 15px;
        }
        .mode-toggle {
            display: flex;
            gap: 12px;
            margin: 20px 0;
        }
        .mode-toggle button {
            flex: 1;
            background-color: #ecf0f1;
            color: #34495e;
        }
        .active-mode {
            background-color: #2ecc71 !important;
            color: white !important;
        }
        .log {
            height: 150px;
            overflow-y: auto;
            background-color: #2c3e50;
            padding: 15px;
            border-radius: 6px;
            font-family: 'Courier New', monospace;
            margin-top: 15px;
            color: #ecf0f1;
            font-size: 14px;
        }
        .log div {
            margin-bottom: 8px;
            line-height: 1.5;
        }
        .log div:last-child {
            margin-bottom: 0;
        }
        .timestamp {
            color: #95a5a6;
        }
        .success {
            color: #2ecc71;
        }
        .warning {
            color: #f39c12;
        }
        .error {
            color: #e74c3c;
        }
        .info {
            color: #3498db;
        }
        .section-title {
            display: flex;
            align-items: center;
            margin-bottom: 15px;
        }
        .section-title h3 {
            margin: 0;
            flex: 1;
        }
        .section-title .status {
            background-color: #3498db;
            color: white;
            padding: 5px 10px;
            border-radius: 20px;
            font-size: 12px;
            font-weight: bold;
        }
        .status-not-started {
            background-color: #95a5a6;
        }
        .status-in-progress {
            background-color: #f39c12;
        }
        .status-completed {
            background-color: #2ecc71;
        }
        .position-mapping {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 15px;
        }
        .position-item {
            display: flex;
            align-items: center;
            background-color: #e0f7fa;
            padding: 5px 10px;
            border-radius: 4px;
            font-family: monospace;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>列表排序算法可视化工具（修正版）</h1>
        <p>演示如何按照指定子集顺序重新排列列表元素，解决元素重复问题</p>
        
        <div class="config-panel">
            <h3>配置参数</h3>
            <div class="input-group">
                <label for="originalListInput">原始列表（用逗号分隔，如 a,b,c,d）:</label>
                <input type="text" id="originalListInput" value="a,b,c,d,e,f,g,h,i,j,k,l,m,n">
            </div>
            <div class="input-group">
                <label for="subsetOrderInput">子集顺序（用逗号分隔，如 c,a,b）:</label>
                <input type="text" id="subsetOrderInput" value="c,a,b">
            </div>
            <div class="mode-toggle">
                <button id="manualModeBtn" class="active-mode">手动模式</button>
                <button id="autoModeBtn">自动模式</button>
            </div>
            <div class="speed-control" id="speedControl" style="display: none;">
                <label>动画速度:</label>
                <input type="range" id="speedSlider" min="100" max="2000" value="800" step="100">
                <span id="speedValue">800ms</span>
            </div>
            <div class="controls">
                <button id="startBtn">开始演示</button>
                <button id="nextBtn" disabled>下一步</button>
                <button id="prevBtn" disabled>上一步</button>
                <button id="resetBtn">重置</button>
            </div>
        </div>
        
        <div>
            <div class="section-title">
                <h3>原始列表:</h3>
                <div class="status status-not-started" id="originalStatus">未开始</div>
            </div>
            <div class="list-container original" id="originalList"></div>
        </div>
        
        <div>
            <div class="section-title">
                <h3>子集顺序:</h3>
                <div class="status status-not-started" id="subsetStatus">未开始</div>
            </div>
            <div class="list-container subset" id="subsetOrder"></div>
        </div>
        
        <div>
            <div class="section-title">
                <h3>结果列表:</h3>
                <div class="status status-not-started" id="resultStatus">未开始</div>
            </div>
            <div class="list-container result" id="resultList"></div>
            <div id="positionMapping" class="position-mapping"></div>
        </div>
        
        <div class="explanation">
            <h3>算法执行过程:</h3>
            <div class="log" id="algorithmLog"></div>
            <div id="currentStep"></div>
        </div>
        
        <div class="explanation">
            <h3>算法原理说明（修正版）:</h3>
            <p>本算法实现了以下功能：</p>
            <ol>
                <li>保持非子集元素的原始顺序不变</li>
                <li>将子集元素按照指定的新顺序重新排列</li>
                <li><strong>修正问题</strong>：确保子集元素不会被重复处理</li>
                <li>最终结果中子集元素连续出现</li>
            </ol>
            <p><strong>核心改进：</strong></p>
            <ul>
                <li>新增<code>已处理子集元素</code>集合，跟踪已处理的元素</li>
                <li>在添加每个子集元素前检查是否已处理</li>
                <li>优化剩余元素处理逻辑，防止重复添加</li>
            </ul>
            <p><strong>算法步骤：</strong></p>
            <ol>
                <li>初始化指针和空结果列表</li>
                <li>创建已处理元素集合</li>
                <li>遍历原始列表中的每个元素：
                    <ul>
                        <li>非子集元素：直接加入结果</li>
                        <li>子集元素：检查是否已处理，若未处理则按新顺序处理</li>
                    </ul>
                </li>
                <li>处理完原始列表后，检查并添加剩余未处理的子集元素</li>
                <li>生成最终的位置映射表</li>
            </ol>
        </div>
    </div>

    <script>
        // 配置参数
        let 原始列表 = [];
        let 子集顺序 = [];
        let 子集集合 = new Set();
        let 当前步骤 = 0;
        let 结果列表 = [];
        let 原始指针 = 0;
        let 子集指针 = 0;
        let 高亮元素 = [];
        let 自动模式 = false;
        let 动画间隔 = 800;
        let 动画计时器 = null;
        let 步骤历史 = [];
        let 当前演示状态 = '未开始'; // '未开始' | '进行中' | '已完成'
        let 已处理子集元素 = new Set(); // 新增：跟踪已处理的子集元素

        // DOM元素
        const 原始列表元素 = document.getElementById('originalList');
        const 子集顺序元素 = document.getElementById('subsetOrder');
        const 结果列表元素 = document.getElementById('resultList');
        const 算法日志元素 = document.getElementById('algorithmLog');
        const 当前步骤元素 = document.getElementById('currentStep');
        const 原始列表输入 = document.getElementById('originalListInput');
        const 子集顺序输入 = document.getElementById('subsetOrderInput');
        const 速度滑块 = document.getElementById('speedSlider');
        const 速度显示 = document.getElementById('speedValue');
        const 手动模式按钮 = document.getElementById('manualModeBtn');
        const 自动模式按钮 = document.getElementById('autoModeBtn');
        const 速度控制面板 = document.getElementById('speedControl');
        const 开始按钮 = document.getElementById('startBtn');
        const 下一步按钮 = document.getElementById('nextBtn');
        const 上一步按钮 = document.getElementById('prevBtn');
        const 重置按钮 = document.getElementById('resetBtn');
        const 原始状态显示 = document.getElementById('originalStatus');
        const 子集状态显示 = document.getElementById('subsetStatus');
        const 结果状态显示 = document.getElementById('resultStatus');
        const 位置映射显示 = document.getElementById('positionMapping');

        // 事件监听
        开始按钮.addEventListener('click', 开始演示);
        下一步按钮.addEventListener('click', 下一步);
        上一步按钮.addEventListener('click', 上一步);
        重置按钮.addEventListener('click', 重置);
        手动模式按钮.addEventListener('click', () => 切换模式('manual'));
        自动模式按钮.addEventListener('click', () => 切换模式('auto'));
        速度滑块.addEventListener('input', 更新速度);

        // 初始化
        更新界面();

        function 切换模式(模式) {
            自动模式 = (模式 === 'auto');
            手动模式按钮.classList.toggle('active-mode', !自动模式);
            自动模式按钮.classList.toggle('active-mode', 自动模式);
            速度控制面板.style.display = 自动模式 ? 'flex' : 'none';
            
            if (当前演示状态 === '进行中' && 自动模式) {
                开始自动演示();
            } else {
                停止自动演示();
            }
        }

        function 更新速度() {
            动画间隔 = parseInt(速度滑块.value);
            速度显示.textContent = 动画间隔 + 'ms';
        }

        function 开始演示() {
            // 解析输入
            const 原始输入 = 原始列表输入.value.trim().split(',').map(s => s.trim());
            const 子集输入 = 子集顺序输入.value.trim().split(',').map(s => s.trim());
            
            if (原始输入.length === 0 || 子集输入.length === 0) {
                添加日志('错误：请输入有效的列表和子集顺序！', 'error');
                return;
            }
            
            // 检查子集元素是否都在原始列表中
            const 无效子集元素 = 子集输入.filter(c => !原始输入.includes(c));
            if (无效子集元素.length > 0) {
                添加日志(`错误：子集元素 ${无效子集元素.join(',')} 不在原始列表中`, 'error');
                return;
            }
            
            // 初始化数据
            原始列表 = 原始输入;
            子集顺序 = 子集输入;
            子集集合 = new Set(子集顺序);
            当前步骤 = 0;
            结果列表 = [];
            原始指针 = 0;
            子集指针 = 0;
            高亮元素 = [];
            步骤历史 = [];
            已处理子集元素 = new Set(); // 重置已处理集合
            当前演示状态 = '进行中';
            位置映射显示.innerHTML = '';
            
            添加日志('初始化算法...', 'info');
            添加日志(`原始列表: [${原始列表.join(', ')}]`, 'info');
            添加日志(`子集顺序: [${子集顺序.join(', ')}]`, 'info');
            添加日志('创建已处理子集元素集合，防止重复处理', 'success');
            
            更新界面();
            
            if (自动模式) {
                开始自动演示();
            } else {
                下一步按钮.disabled = false;
            }
        }

        function 开始自动演示() {
            停止自动演示();
            动画计时器 = setInterval(() => {
                if (当前演示状态 === '进行中') {
                    const 是否完成 = 下一步();
                    if (是否完成) {
                        停止自动演示();
                    }
                } else {
                    停止自动演示();
                }
            }, 动画间隔);
        }

        function 停止自动演示() {
            if (动画计时器) {
                clearInterval(动画计时器);
                动画计时器 = null;
            }
        }

        function 下一步() {
            if (当前演示状态 !== '进行中') return true;
            
            // 保存当前状态到历史
            const 当前状态 = {
                结果列表: [...结果列表],
                原始指针,
                子集指针,
                当前步骤,
                已处理子集元素: new Set(已处理子集元素)
            };
            步骤历史.push(当前状态);
            
            if (当前步骤 === 0) {
                // 初始状态
                添加日志('初始化指针和结果列表', 'info');
                添加日志('初始化已处理子集元素集合', 'info');
                当前步骤++;
            } else if (原始指针 < 原始列表.length) {
                const 当前元素 = 原始列表[原始指针];
                高亮元素 = [`original-${原始指针}`];
                
                if (子集集合.has(当前元素)) {
                    if (!已处理子集元素.has(当前元素)) {
                        添加日志(`处理子集元素 '${当前元素}'`, 'info');
                        高亮元素.push(`subset-${子集指针}`);
                        
                        // 处理子集元素
                        while (子集指针 < 子集顺序.length && 
                              子集顺序[子集指针] !== 当前元素) {
                            const 待添加元素 = 子集顺序[子集指针];
                            if (!已处理子集元素.has(待添加元素)) {
                                结果列表.push(待添加元素);
                                已处理子集元素.add(待添加元素);
                                添加日志(`添加子集元素 '${待添加元素}' 到结果`, 'success');
                                当前步骤++;
                                更新界面();
                                return false;
                            }
                            子集指针++;
                        }
                        
                        // 添加匹配的元素
                        if (子集指针 < 子集顺序.length) {
                            结果列表.push(当前元素);
                            已处理子集元素.add(当前元素);
                            添加日志(`添加匹配的子集元素 '${当前元素}'`, 'success');
                            子集指针++;
                        }
                    } else {
                        添加日志(`子集元素 '${当前元素}' 已处理，跳过`, 'warning');
                    }
                } else {
                    // 添加非子集元素
                    结果列表.push(当前元素);
                    添加日志(`添加非子集元素 '${当前元素}'`, 'info');
                }
                
                原始指针++;
                当前步骤++;
            } else if (子集指针 < 子集顺序.length) {
                // 添加剩余的子集元素
                const 剩余元素 = 子集顺序[子集指针];
                if (!已处理子集元素.has(剩余元素)) {
                    高亮元素 = [`subset-${子集指针}`];
                    结果列表.push(剩余元素);
                    已处理子集元素.add(剩余元素);
                    添加日志(`添加剩余子集元素 '${剩余元素}'`, 'success');
                } else {
                    添加日志(`剩余子集元素 '${剩余元素}' 已处理，跳过`, 'warning');
                }
                子集指针++;
                当前步骤++;
            } else {
                // 算法完成
                添加日志('算法完成，生成位置映射', 'success');
                生成位置映射();
                当前演示状态 = '已完成';
                下一步按钮.disabled = true;
                更新界面();
                return true;
            }
            
            更新界面();
            return false;
        }

        function 上一步() {
            if (步骤历史.length === 0) return;
            
            const 上一步状态 = 步骤历史.pop();
            结果列表 = 上一步状态.结果列表;
            原始指针 = 上一步状态.原始指针;
            子集指针 = 上一步状态.子集指针;
            当前步骤 = 上一步状态.当前步骤;
            已处理子集元素 = new Set(上一步状态.已处理子集元素);
            当前演示状态 = '进行中';
            下一步按钮.disabled = false;
            位置映射显示.innerHTML = '';
            
            添加日志('回退到上一步', 'warning');
            更新界面();
        }

        function 重置() {
            停止自动演示();
            当前步骤 = 0;
            结果列表 = [];
            原始指针 = 0;
            子集指针 = 0;
            高亮元素 = [];
            步骤历史 = [];
            已处理子集元素 = new Set();
            当前演示状态 = '未开始';
            算法日志元素.innerHTML = '';
            下一步按钮.disabled = true;
            上一步按钮.disabled = true;
            位置映射显示.innerHTML = '';
            更新界面();
        }

        function 生成位置映射() {
            位置映射显示.innerHTML = '';
            const 映射表 = {};
            
            for (let i = 0; i < 结果列表.length; i++) {
                const 元素 = 结果列表[i];
                映射表[元素] = i + 1;
                
                const 映射项 = document.createElement('div');
                映射项.className = 'position-item';
                映射项.textContent = `${元素}: ${i+1}`;
                位置映射显示.appendChild(映射项);
            }
            
            return 映射表;
        }

        function 更新界面() {
            // 更新状态显示
            原始状态显示.textContent = 当前演示状态 === '未开始' ? '未开始' : 
                                     当前演示状态 === '进行中' ? '处理中' : '已完成';
            原始状态显示.className = 'status ' + (
                当前演示状态 === '未开始' ? 'status-not-started' :
                当前演示状态 === '进行中' ? 'status-in-progress' : 'status-completed'
            );
            
            子集状态显示.textContent = 当前演示状态 === '未开始' ? '未开始' : 
                                     当前演示状态 === '进行中' ? '使用中' : '已完成';
            子集状态显示.className = 'status ' + (
                当前演示状态 === '未开始' ? 'status-not-started' :
                当前演示状态 === '进行中' ? 'status-in-progress' : 'status-completed'
            );
            
            结果状态显示.textContent = 当前演示状态 === '未开始' ? '未开始' : 
                                     当前演示状态 === '进行中' ? '构建中' : '已完成';
            结果状态显示.className = 'status ' + (
                当前演示状态 === '未开始' ? 'status-not-started' :
                当前演示状态 === '进行中' ? 'status-in-progress' : 'status-completed'
            );
            
            // 更新原始列表显示
            原始列表元素.innerHTML = '';
            原始列表.forEach((字符, 索引) => {
                const 元素 = document.createElement('div');
                元素.className = 'item';
                元素.textContent = 字符;
                if (高亮元素.includes(`original-${索引}`)) {
                    元素.classList.add('highlight');
                }
                if (索引 === 原始指针 && 当前步骤 > 0) {
                    const 指针标记 = document.createElement('div');
                    指针标记.className = 'pointer';
                    指针标记.textContent = '原始指针';
                    元素.appendChild(指针标记);
                }
                原始列表元素.appendChild(元素);
            });
            
            // 更新子集顺序显示
            子集顺序元素.innerHTML = '';
            子集顺序.forEach((字符, 索引) => {
                const 元素 = document.createElement('div');
                元素.className = 'item';
                元素.textContent = 字符;
                if (高亮元素.includes(`subset-${索引}`)) {
                    元素.classList.add('highlight');
                }
                if (索引 === 子集指针 && 当前步骤 > 0) {
                    const 指针标记 = document.createElement('div');
                    指针标记.className = 'pointer';
                    指针标记.textContent = '子集指针';
                    元素.appendChild(指针标记);
                }
                子集顺序元素.appendChild(元素);
            });
            
            // 更新结果列表显示
            结果列表元素.innerHTML = '';
            结果列表.forEach((字符, 索引) => {
                const 元素 = document.createElement('div');
                元素.className = 'item added';
                元素.textContent = 字符;
                结果列表元素.appendChild(元素);
            });
            
            // 更新按钮状态
            上一步按钮.disabled = 步骤历史.length === 0 || 自动模式;
            开始按钮.disabled = 当前演示状态 === '进行中';
        }

        function 添加日志(消息, 类型 = 'info') {
            const 时间 = new Date().toLocaleTimeString();
            const 日志条目 = document.createElement('div');
            日志条目.innerHTML = `<span class="timestamp">[${时间}]</span> <span class="${类型}">${消息}</span>`;
            算法日志元素.appendChild(日志条目);
            算法日志元素.scrollTop = 算法日志元素.scrollHeight;
            
            // 更新当前步骤说明
            当前步骤元素.innerHTML = `<strong>当前操作:</strong> <span class="${类型}">${消息}</span>`;
        }
    </script>
</body>
</html>